﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using bitzhuwei.Compiler;

namespace bitzhuwei.ExpFormat {
    public interface IInherit {

        public IInherit? Parent { get; set; }

        public IEnumerable<IInherit> ReversedChildren();

        public void PrintSelf(TextWriter writer, IReadOnlyList<string>? stArray);

        public class TokenWrap : IInherit {
            private readonly Token token;

            public TokenWrap(Token token) {
                this.token = token;
            }

            public IInherit? Parent { get; set; }

            public IEnumerable<IInherit> ReversedChildren() {
                yield break;
            }
            public void PrintSelf(TextWriter writer, IReadOnlyList<string>? stArray) {
                //this.token.Print(writer, stArray);
                string strType;
                if (stArray != null) { strType = stArray[this.token.kind]; }
                else { strType = this.token.kind.ToString(); }
                writer.Write($"T[{this.token.index}]={strType} {this.token.value}");
            }

            public static bool operator ==(TokenWrap left, TokenWrap right) {
                if (object.ReferenceEquals(left, right)) { return true; }
                if (left is null || right is null) { return false; }

                return left.token == right.token;
            }
            public static bool operator !=(TokenWrap left, TokenWrap right) {
                return !(left == right);
            }
            public override bool Equals(object? obj) {
                if (obj is TokenWrap other) {
                    return this.token.Equals(other.token);
                }
                else { return false; }
            }

            public override int GetHashCode() {
                return this.token.GetHashCode();
            }

            public override string ToString() {
                var builder = new StringBuilder();
                using (var writer = new StringWriter(builder)) { this.PrintSelf(writer, null); }
                return builder.ToString();
            }
        }

        public static void Print(IInherit self, TextWriter writer, IReadOnlyList<string> stArray) {
            var stack = new Stack<IInherit>(); stack.Push(self);
            while (stack.Count > 0) {
                var node = stack.Pop();
                PrintPremark(node, writer);
                node.PrintSelf(writer, stArray);
                writer.WriteLine();

                foreach (var child in node.ReversedChildren()) {
                    stack.Push(child);
                }
            }
        }
        public static void PrintPremark(IInherit self, TextWriter writer) {
            var parent = self.Parent; if (parent is null) { return; }

            var lstLine = new List<bool>();
            while (parent != null) {
                var pp = parent.Parent;
                if (pp != null) {
                    lstLine.Add(LastChild(parent));
                }
                parent = pp;
            }
            for (int i = lstLine.Count - 1; i >= 0; i--) {
                if (lstLine[i]) { writer.Write("   "); }
                else { writer.Write(" │ "); }
            }

            if (LastChild(self)) {
                writer.Write(" └─");
            }
            else {
                writer.Write(" ├─");
            }
        }


        /// <summary>
        /// Am I the last child?
        /// </summary>
        public static bool LastChild(IInherit self) {
            var parent = self.Parent; if (parent is null) { throw new Exception(); }
            foreach (var child in parent.ReversedChildren()) {
                //if (child.Equals(self)) { return true; }
                //else { return false; }
                if (self.Equals(child)) { return true; }
                else { return false; }
            }

            throw new Exception();
        }

    }
}
